﻿using LinnerToolkit.Core.Buffer;
using LinnerToolkit.Core.Codec;
using LinnerToolkit.MindBody.Core.Codec;
using SimulationPTSystem.Evaluation.Enums;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SimulationPTSystem.Evaluation.Codec
{
    public class DataDecoder : DataDecoderBase
    {
        private const int HEADER_LENGTH = 1;

        private const int MACHINE_ID_LENGTH = 1;

        private const int HID_LENGTH = 1;

        private const int HEART_RATE_LENGTH = 16;

        private const int BID_LENGTH = 1;

        private const int BREATHE_LENGTH = 8;

        private const int PULSE_STATE_LENGTH = 1;

        private const int RESERVE_LENGTH = 2;

        private const int MACHINE_STATE_LENGTH = 1;

        private const int BREATHE_STATE_LENGTH = 1;

        private const int END_LENGTH = 1;

        private const int LENGTH = HEADER_LENGTH + MACHINE_ID_LENGTH + HID_LENGTH + HEART_RATE_LENGTH + BID_LENGTH + BREATHE_LENGTH + PULSE_STATE_LENGTH + RESERVE_LENGTH + MACHINE_STATE_LENGTH + BREATHE_STATE_LENGTH + END_LENGTH;

        private const int MACHINE_ID_INDEX = 0;

        private const int HID_INDEX = MACHINE_ID_LENGTH;

        private const int HEART_RATE_INDEX = HID_INDEX + HID_LENGTH;

        private const int BID_INDEX = HEART_RATE_INDEX + HEART_RATE_LENGTH;

        private const int BREATHE_INDEX = BID_INDEX + BID_LENGTH;

        private const int PULSE_STATE_INDEX = BREATHE_INDEX + BREATHE_LENGTH;

        private const int RESERVE_INDEX = PULSE_STATE_INDEX + PULSE_STATE_LENGTH;

        private const int MACHINE_STATE_INDEX = RESERVE_INDEX + RESERVE_LENGTH;

        private const int BREATHE_STATE_INDEX = MACHINE_STATE_INDEX + MACHINE_STATE_LENGTH;

        private const byte IS_IN_HALE = 0x00;

        public DataDecoder()
        {
            BYTE_HEADER = new byte[] { 0xAA };
            BYTE_END = new byte[] { 0xCC };
            MinDecodableLength = LENGTH;
        }

        protected override IDecodeData Decode(DataBuffer buffer)
        {
            if (!CheckHeader(buffer, BYTE_HEADER))
                return null;

            buffer.Skip(1, false);
            byte[] remainingBytes = new byte[LENGTH - 1];
            buffer.Get(remainingBytes, 0, remainingBytes.Length);
            if (!CheckEnd(remainingBytes, BYTE_END))
                return null;

            SensorData sensorData = new SensorData()
            {
                MachineID = remainingBytes[MACHINE_ID_INDEX],
                HID = remainingBytes[HID_INDEX],
                PulseCycles = new List<int>(),
                BID = remainingBytes[BID_INDEX],
                InspiratoryTimes = new List<int>(),
                ExpiratoryTimes = new List<int>(),
                PulseState = (remainingBytes[PULSE_STATE_INDEX].Equals(1) ? PulseState.Normal : PulseState.Interrupt),
                ControllerID = remainingBytes[RESERVE_INDEX],
                IsAPlayerWork = Convert.ToBoolean(remainingBytes[MACHINE_STATE_INDEX] & 0x01),
                IsBPlayerWork = Convert.ToBoolean(remainingBytes[MACHINE_STATE_INDEX] & 0x02),
                IsMP3State = Convert.ToBoolean(remainingBytes[MACHINE_STATE_INDEX] & 0x04),
                RaiseHand = Convert.ToBoolean(remainingBytes[MACHINE_STATE_INDEX] & 0x08),
                IsInHale = remainingBytes[BREATHE_STATE_INDEX].Equals(IS_IN_HALE) ? true : false,
            };
            for (int i = 0; i < HEART_RATE_LENGTH; i += 2)
            {
                int pulseCycle = remainingBytes[HEART_RATE_INDEX + i] + (remainingBytes[HEART_RATE_INDEX + i + 1] << 8);
                if (pulseCycle == 0)
                    break;
                sensorData.PulseCycles.Add(pulseCycle);
            }
            for (int i = 0; i < BREATHE_LENGTH; i += 2)
            {
                int inspiratoryTime = remainingBytes[BREATHE_INDEX + i];
                int expiratoryTime = remainingBytes[BREATHE_INDEX + i + 1];
                if (inspiratoryTime == 0 && expiratoryTime == 0)
                    break;
                sensorData.InspiratoryTimes.Add(inspiratoryTime);
                sensorData.ExpiratoryTimes.Add(expiratoryTime);
            }

            return sensorData;
        }
    }
}
